package com.yunda.nebula.gateway.sync.nacos;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.yunda.nebula.gateway.core.DataChangedListener;
import com.yunda.nebula.gateway.core.SyncDataService;
import com.yunda.nebula.gateway.core.entity.ConfigData;
import com.yunda.nebula.gateway.core.enums.DataEventTypeEnum;
import com.yunda.nebula.gateway.core.exception.GateWayException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * \* User: wei.zhang
 * \* Date: 2020/9/18 11:54
 * \* Description:
 *
 * @author wei.zhang
 * \
 */
@Service
@Slf4j
public class NacosSyncDataService implements SyncDataService {

    private NacosConfig nacosConfig;

    private ConfigService configService = null;

    public static final String SEP1 = "-";

    public static final String APPID = "appid";


    private DataChangedListener dataChangedListener;

    private Map<String, Listener> listenerMap = new ConcurrentHashMap<>();

    public NacosSyncDataService(NacosConfig nacosConfig, DataChangedListener dataChangedListener) {
        this.nacosConfig = nacosConfig;
        this.dataChangedListener = dataChangedListener;
        this.start(nacosConfig);
    }

    public boolean start(NacosConfig nacosConfig) {

        List<String> list = fetchAllApps();

        try {
            String dataid = getDataId(APPID);
            AppIdRefreshListener appIdRefreshListener = new AppIdRefreshListener(this);
            configService.addListener(dataid, nacosConfig.getGroup(), appIdRefreshListener);
            listenerMap.put(dataid, appIdRefreshListener);

        } catch (NacosException e) {
            log.error("create nacos listener failed",e);
        }
        if (!CollectionUtils.isEmpty(list)){
            list.forEach(appid -> doFetchAppConfig(appid, DataEventTypeEnum.CREATE));
        }

        return true;
    }


    @Override
    public void doFetchAppConfig(String app, DataEventTypeEnum eventTypeEnum) {
        ConfigService configService = getConfigService();
        String dataid = getDataId(app);

        try {
            String config = configService.getConfig(dataid, nacosConfig.getGroup(), nacosConfig.getTimeout());
            ConfigData configData = JSONObject.parseObject(config,ConfigData.class);

            dataChangedListener.onAppChanged(configData,eventTypeEnum);

            if (DataEventTypeEnum.CREATE.equals(eventTypeEnum)){
                AppInfoRefreshListener appInfoRefreshListener = new AppInfoRefreshListener(this);
                try {
                    configService.addListener(dataid, nacosConfig.getGroup(), appInfoRefreshListener);

                } catch (NacosException e) {
                    e.printStackTrace();
                }
                listenerMap.put(dataid, appInfoRefreshListener);
            }

        } catch (NacosException e) {
            log.error("get nacos appInfo failed",e);
        }
    }

    @Override
    public void doRemoveAppConfig(String appId, DataEventTypeEnum delete) {
        String dataId = getDataId(appId);
        getConfigService().removeListener(dataId,nacosConfig.getGroup(),listenerMap.get(dataId));
        listenerMap.remove(dataId);

        ConfigData configData = new ConfigData();
        configData.setAppId(appId);
        dataChangedListener.onAppChanged(configData,delete);

    }

    @Override
    public List<String> fetchAllApps() {
        ConfigService configService = getConfigService();
        String dataid = getDataId(APPID);

        String config;
        List<String> appList = null;
        try {
            config = configService.getConfig(dataid, nacosConfig.getGroup(), 10000L);
            appList = JSONObject.parseArray(config, String.class);

        } catch (NacosException e) {
            log.error("get nacos appid failed",e);
        }

        return appList;
    }

    public String getDataId(String appid){
        return nacosConfig.getPrefix() + SEP1 + appid;
    }

    public DataChangedListener getDataChangedListener(){
        return this.dataChangedListener;
    }

    public Map<String, Listener> getListenerMap(){
        return listenerMap;
    }

    ConfigService createConfigService(NacosConfig nacosConfig) {
        if (Objects.isNull(configService)) {
            synchronized (NacosSyncDataService.class) {
                if (Objects.isNull(configService)) {
                    try {
                        configService = NacosFactory.createConfigService(
                                nacosConfig.getConfigServiceProperties());
                    } catch (NacosException e) {
                        log.error(e.getMessage());
                        String message = String.format("nacos %s create configService failed", nacosConfig.getNamespace());
                        throw new GateWayException(message, e);
                    }
                }


            }
        }
        return configService;
    }

    public ConfigService getConfigService() {
        if (Objects.isNull(configService)) {
            createConfigService(this.nacosConfig);
        }
        return configService;
    }
}
